package com.erbadagang.springboot.dsa;

import org.apache.commons.codec.binary.Base64;

import java.security.*;
import java.security.interfaces.DSAPrivateKey;
import java.security.interfaces.DSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.Map;

/**
 * @description 常用数字签名算法DSA，如支付系统的核心数据签名。
 * @ClassName: DSACoder
 * @author: 郭秀志 jbcode@126.com
 * @date: 2020/7/25 10:06
 * @Copyright:
 */
public class DSACoder {
    //数字签名，密钥算法
    public static final String KEY_ALGORITHM = "DSA";

    /**
     * 数字签名
     * 签名/验证算法
     */
    public static final String SIGNATURE_ALGORITHM = "SHA1withDSA";

    /**
     * DSA密钥长度，密钥长度必须是64的倍数，在512到1024位之间
     */
    private static final int KEY_SIZE = 1024;
    //公钥
    private static final String PUBLIC_KEY = "DSAPublicKey";

    //私钥
    private static final String PRIVATE_KEY = "DSAPrivateKey";

    /**
     * 初始化密钥对
     *
     * @return Map 甲方密钥的Map
     */
    public static Map<String, Object> initKey() throws Exception {
        //实例化密钥生成器
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(KEY_ALGORITHM);
        //初始化密钥生成器
        keyPairGenerator.initialize(KEY_SIZE, new SecureRandom());
        //生成密钥对
        KeyPair keyPair = keyPairGenerator.generateKeyPair();
        //甲方公钥
        DSAPublicKey publicKey = (DSAPublicKey) keyPair.getPublic();
        //甲方私钥
        DSAPrivateKey privateKey = (DSAPrivateKey) keyPair.getPrivate();
        //将密钥存储在map中
        Map<String, Object> keyMap = new HashMap<String, Object>();
        keyMap.put(PUBLIC_KEY, publicKey);
        keyMap.put(PRIVATE_KEY, privateKey);
        return keyMap;

    }


    /**
     * 签名
     *
     * @param privateKey 密钥
     * @return byte[] 数字签名
     */
    public static byte[] sign(byte[] data, byte[] privateKey) throws Exception {

        //取得私钥
        PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(privateKey);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        //生成私钥
        PrivateKey priKey = keyFactory.generatePrivate(pkcs8KeySpec);
        //实例化Signature
        Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
        //初始化Signature
        signature.initSign(priKey);
        //更新
        signature.update(data);
        return signature.sign();
    }

    /**
     * 校验数字签名
     *
     * @param data      待校验数据
     * @param publicKey 公钥
     * @param sign      数字签名
     * @return boolean 校验成功返回true，失败返回false
     */
    public static boolean verify(byte[] data, byte[] publicKey, byte[] sign) throws Exception {
        //转换公钥材料
        //实例化密钥工厂
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        //初始化公钥
        //密钥材料转换
        X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(publicKey);
        //产生公钥
        PublicKey pubKey = keyFactory.generatePublic(x509KeySpec);
        //实例化Signature
        Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
        //初始化Signature
        signature.initVerify(pubKey);
        //更新
        signature.update(data);
        //验证
        return signature.verify(sign);
    }

    /**
     * 取得私钥
     *
     * @param keyMap 密钥map
     * @return byte[] 私钥
     */
    public static byte[] getPrivateKey(Map<String, Object> keyMap) {
        Key key = (Key) keyMap.get(PRIVATE_KEY);
        return key.getEncoded();
    }

    /**
     * 取得公钥
     *
     * @param keyMap 密钥map
     * @return byte[] 公钥
     */
    public static byte[] getPublicKey(Map<String, Object> keyMap) throws Exception {
        Key key = (Key) keyMap.get(PUBLIC_KEY);
        return key.getEncoded();
    }

    /**
     * @param args
     * @throws Exception
     */
    public static void main(String[] args) throws Exception {
        System.out.println("================生成密钥对,发送方将公钥公布给接收方，开始进行加密数据签名和传输,接收方使用公钥验证甲方签名=============");

        //初始化密钥，生成密钥对
        Map<String, Object> keyMap = DSACoder.initKey();
        //公钥
        byte[] publicKey = DSACoder.getPublicKey(keyMap);
        System.out.println("公钥：" + Base64.encodeBase64String(publicKey));

        //私钥
        byte[] privateKey = DSACoder.getPrivateKey(keyMap);
        System.out.println("私钥：" + Base64.encodeBase64String(privateKey));

        String originalStr = "orderID=202007258888&amount=88.88&DSA数字签名算法 By guoxiuzhi";
        System.out.println("待签名原文:" + originalStr);

        //发送方通过私钥进行数据的签名
        byte[] sign = DSACoder.sign(originalStr.getBytes(), privateKey);
        String signString = Base64.encodeBase64String(sign);
        System.out.println("产生签名：" + signString);

        //接收方使用发送方公钥验证发送方签名
        boolean status = DSACoder.verify(originalStr.getBytes(), publicKey, Base64.decodeBase64(signString));//为了掩饰传输过来的字符串使用了Base64.decodeBase64(signString)，本例可以直接使用变量sign。
        System.out.println("状态：" + status);

        /**
         * 公私钥以字符串方式存储测试。
         * 主要方法：  Base64.decodeBase64转化String为byte[]; Base64.encodeBase64String转化字符串。
         */
        String publicKeyStr = "MIIBtzCCASwGByqGSM44BAEwggEfAoGBAP1/U4EddRIpUt9KnC7s5Of2EbdSPO9EAMMeP4C2USZpRV1AIlH7WT2NWPq/xfW6MPbLm1Vs14E7gB00b/JmYLdrmVClpJ+f6AR7ECLCT7up1/63xhv4O1fnxqimFQ8E+4P208UewwI1VBNaFpEy9nXzrith1yrv8iIDGZ3RSAHHAhUAl2BQjxUjC8yykrmCouuEC/BYHPUCgYEA9+GghdabPd7LvKtcNrhXuXmUr7v6OuqC+VdMCz0HgmdRWVeOutRZT+ZxBxCBgLRJFnEj6EwoFhO3zwkyjMim4TwWeotUfI0o4KOuHiuzpnWRbqN/C/ohNWLx+2J6ASQ7zKTxvqhRkImog9/hWuWfBpKLZl6Ae1UlZAFMO/7PSSoDgYQAAoGAepocw+Bi1W+3MEZkH6Ab4D/wzO36fHoGrtbtdOnS1bkE1oRNrdmvPaFEoHiFQwWaGgdfYgXEtGCh5qwoMfMG7VbuTbPzXB8sBUysRVantZE9Y22tRyC1Tzs/RPRxzvvURs3AC25DXpVxKjt9m/WpJ9G5XchE3+bR4beUiY5hZ80=";
        publicKey = Base64.decodeBase64(publicKeyStr);
        System.out.println("公钥：" + Base64.encodeBase64String(publicKey));
    }
}